
/**
 * 描述 Vue 或者uni-app H5项目服务端更新后自动检查刷新
 * 时间：2023-11-11
 */

const _window = window as any;

/* 是否在弹框状态 */
let timeoutID: any = 0, isMessageBox = false;



/* 获取html中js资源名称 */
const WithdrawLinks = async (html: string): Promise<string[]> => {
  /* 这段正则全局匹配可以放到外面去，注意lastIndex即可 */
  const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm;
  const linkReg = /<link.*href=["'](?<href>[^"']+)/gm;
  /* 如果这段正则写在外面并且全局匹配的时候，此时需要重0的位置开始匹配 */
  // scriptReg.lastIndex = 0;
  let result: Array<string> = [], link: Array<string> = [], match: any;
  while ((match = scriptReg.exec(html))) {
    result.push(match.groups.src);
  }

  while ((match = linkReg.exec(html))) {
    link.push(match.groups.href);
  }


  // result = result.filter(element => !element.startsWith('chrome-extension://'));
  // result = result.filter((element) => !element.match(/^http(s?):\/\/[^ ]+/g));


  const eliminate = <T>(result: string[]): string[] => {
    let filterList: string[] = [];
    filterList = result.filter((element) => !element.startsWith('chrome-extension://'));   // 过滤浏览器插件导入的JS
    filterList = result.filter((element) => !element.match(/^http(s?):\/\/[^ ]+/g)); // 过滤网络协议的JS资源，只留本项目的资源
    return filterList
  }

  return [...eliminate(link), ...eliminate(result)];

}

// 检查更新
async function CheckForUpdates() {
  /* 这里加不加时间戳的取决于Nginx的缓存配置
    比如：Nginx配置add_header Cache-Control "no-cache"，此时浏览器不直接使用缓存，而是每次向服务器
    发送请求验证资源是否过期，如果没有过期使用缓存中的数据，如果过期了重新获取服务器中的数据。那么这里加不加时间戳
    就没有意义了。  

    再比如：Nginx配置add_header Cache-Control "max-age=86400";缓存一天（可以设置长一点，我记得默认都很久的，
    js,css15天的，图片30天，html没有设置缓存时间），此时，在一天之内
    浏览器获取缓存中的数据，那么如果服务器中的数据发生了变化，此时加上时间戳就是有意义的了，因为不加时间戳，
    缓存没有过期的时候请求index.html文件的时候，还是取缓存中的数据，加上时间戳，浏览器会将其视为新的资源
    重新下载。

    不过这里还有一个问题，就是如果用户关闭了页面，服务器资源更新了，用户再次打开浏览器访问页面，此时由于第一次访问lastScripts
    为空数组，那么通过后续的代码就无法判断页面需要更新了，因为第一次获取的值和后续获取的值是一样的。
    TODO：还没想好下面的解决方案还需要再想想
    解决方案：lastScripts先从localStorage中获取数据，如果有值，就可以判断之前的数据和最新的请求数据对应的js是否相同了。
    那么问题又来了，当资源过期了，我那么直接获取服务器端的数据，而localStorage中又是之前的资源，就会导致页面中会弹框提示说
    页面会有更新，其实这个时候是最新的资源了，没必要进行更新了。

    这样的配置一般不使用add_header Cache-Control "no-store";即浏览器不缓存任何数据

    分析到这里，那么Nginx配置add_header Cache-Control "max-age=86400"（时间再长点），请求的时候再加上时间戳才是最优的解决方案。
  */


  // const newScripts = await WithdrawLinks(await fetch('/?_timestamp=' + Date.now()).then(resp => resp.text()));
  const HaulAwayHTML = await WithdrawLinks(await fetch('/').then(resp => resp.text()));
  const renderHTML = await WithdrawLinks(document.documentElement.innerHTML)
  let result = false;
  console.log(HaulAwayHTML, renderHTML);
  if (renderHTML.length !== HaulAwayHTML.length) {
    result = true;
  } else {
    for (let i = 0; i < HaulAwayHTML.length; i++) {
      if (renderHTML[i] !== HaulAwayHTML[i]) {
        result = true;
        break;
      }
    }
  }
  return result;
}

//#ifdef H5
/* 当其选项卡的内容变得可见或被隐藏时，会在文档上触发 visibilitychange (能见度更改) 事件。 */
document.addEventListener("visibilitychange", function () {
  const status = document.visibilityState
  if (status === 'hidden') {
    timeoutID && clearTimeout(timeoutID);
    timeoutID = null;
  } else if (status === 'visible') {
    /* 弹框状态下就不去做自动刷新函数的调用 */
    !isMessageBox && autoRefresh();
  }
});
//#endif



// 调用即可
export const autoRefresh = async () => {
  //#ifdef H5
  timeoutID = setTimeout(async () => {
    const willUpdate = await CheckForUpdates();
    if (willUpdate) {
      clearTimeout(timeoutID);
      isMessageBox = true;
      const confirm: boolean = window.confirm('页面有更新，点击确定刷新页面?')
      if (confirm) { _window.location.reload(true); } else {
        isMessageBox = false;
        autoRefresh();
      }
    } else {
      autoRefresh();
    }
  }, 10000);
  //#endif
}

